home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmbase-grok-1.2 / query.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  7KB  |  256 lines

  1. /*
  2.  * Do a query into the database, and store a query list into the card. A
  3.  * query list is an 1D array of row numbers in the database. Also set the
  4.  * number of cards from the query in nquery, and pick the card to be
  5.  * displayed first by setting qcurr.
  6.  *
  7.  *    query_none(card)
  8.  *    query_all(card)
  9.  *    query_search(card, string)
  10.  *    query_letter(card, letter)
  11.  *    query_eval(card, expr)
  12.  */
  13.  
  14. #include "config.h"
  15. #include <X11/Xos.h>
  16. #include <stdlib.h>
  17. #include <errno.h>
  18. #include <Xm/Xm.h>
  19. #include "grok.h"
  20. #include "form.h"
  21. #include "proto.h"
  22.  
  23. #define SECT_OK(db,r) ((db)->currsect < 0 ||\
  24.                (db)->currsect == (db)->row[r]->section)
  25.  
  26. static BOOL alloc_query(CARD *, char **);
  27.  
  28. extern Widget    toplevel;    /* top-level shell for icon name */
  29. extern int    col_sorted_by;    /* dbase is sorted by this column */
  30. extern struct    pref pref;    /* global preferences */
  31.  
  32.  
  33. /*
  34.  * delete query, summary becomes empty
  35.  */
  36.  
  37. void query_none(
  38.     CARD        *card)        /* database and form */
  39. {
  40.     if (card->query)                /* clear old query */
  41.         free((void *)card->query);
  42.     card->query  = 0;
  43.     card->nquery = 0;
  44.     card->qcurr  = 0;
  45. }
  46.  
  47.  
  48. /*
  49.  * make sense out of the string, and do the appropriate search
  50.  */
  51.  
  52. void query_any(
  53.     CARD        *card,        /* database and form */
  54.     char        *string)    /* query string */
  55. {
  56.     if (!string || *string == '*' && !string[1])
  57.         query_all(card);
  58.     else if (*string == '(' || *string == '{')
  59.         query_eval(card, string);
  60.     else if (*string == '/')
  61.         query_search(card, string+1);
  62.     else
  63.         query_search(card, string);
  64. }
  65.  
  66.  
  67. /*
  68.  * put all cards into the summary
  69.  */
  70.  
  71. void query_all(
  72.     CARD        *card)        /* database and form */
  73. {
  74.     int        r, n;
  75.  
  76.     if (!alloc_query(card, 0))
  77.         return;
  78.     for (r=n=0; r < card->dbase->nrows; r++)
  79.         if (SECT_OK(card->dbase, r))
  80.             card->query[n++] = r;
  81.     card->nquery = n;
  82. }
  83.  
  84.  
  85. /*
  86.  * put all cards into the summary that contain a search string in any of
  87.  * the searchable items. The search is case-insensitive. This code will
  88.  * gleefully fail if run on a non-ascii system.
  89.  */
  90.  
  91. void query_search(
  92.     CARD        *card,        /* database and form */
  93.     char        *string)    /* string to search for */
  94. {
  95.     int        r;        /* row counter */
  96.     int        i;        /* item counter */
  97.     ITEM        *item;        /* item to check */
  98.     char        *data;        /* database string to test */
  99.     char        *mask;        /* for skipping unselected cards */
  100.     char        search[1024];    /* lower-case search string */
  101.     register char    *p, *q;        /* copy and comparison pointers */
  102.     register char    bit5 = 0x20;    /* bit mask to smash case */
  103.     BOOL        matched;
  104.  
  105.     if (!alloc_query(card, &mask))
  106.         return;
  107.     for (p=string, q=search, i=0; *p && i < sizeof(search)-1; i++)
  108.         *q++ = *p++ | bit5;
  109.     *q = 0;
  110.     for (r=0; r < card->dbase->nrows; r++)
  111.         if (SECT_OK(card->dbase, r) && (!mask || mask[r])) {
  112.             matched = FALSE;
  113.             for (i=0; !matched && i < card->form->nitems; i++) {
  114.                 item = card->form->items[i];
  115.                 if (!IN_DBASE(item->type) || !item->search)
  116.                     continue;
  117.                 if (!(data = dbase_get(card->dbase, r,
  118.                             item->column)))
  119.                     continue;
  120.                 for (p=data; *p; p++) {
  121.                     if ((*p | bit5) != *search)
  122.                         continue;
  123.                     for (q=search; *q; q++, p++)
  124.                         if ((*p | bit5) != *q)
  125.                             break;
  126.                     if (!*q) {
  127.                         card->query[card->nquery++] =r;
  128.                         matched = TRUE;
  129.                         break;
  130.                     }
  131.                     p -= q - search;
  132.                 }
  133.             }
  134.         }
  135.     if (mask)
  136.         free(mask);
  137. }
  138.  
  139.  
  140. /*
  141.  * put all cards into the summary whose last-sorted-by column begins with
  142.  * <letter>: 0..25=letter, 26=misc, 27=all. The search is case-insensitive.
  143.  */
  144.  
  145. #define BLANK(c) ((c) && strchr("\t\n \"!#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (c)))
  146.  
  147. void query_letter(
  148.     CARD        *card,        /* database and form */
  149.     int        letter)        /* 0=0..9, 1..26=a..z, 27=all */
  150. {
  151.     int        r;        /* row counter */
  152.     char        *data;        /* database string to test */
  153.     char        *mask;        /* for skipping unselected cards */
  154.  
  155.     if (letter == 27) {
  156.         query_all(card);
  157.         return;
  158.     }
  159.     if (!alloc_query(card, &mask))
  160.         return;
  161.     letter = letter == 26 ? 0 : letter+'A';
  162.     if (col_sorted_by >= card->dbase->maxcolumns-1)
  163.         col_sorted_by = 0;
  164.     for (r=0; r < card->dbase->nrows; r++)
  165.         if (SECT_OK(card->dbase, r) && (!mask || mask[r])) {
  166.             data = dbase_get(card->dbase, r, col_sorted_by);
  167.             while (data && BLANK(*data))
  168.                 data++;
  169.             if ((!data || !*data || strchr("0123456789_", *data))
  170.                                 && !letter)
  171.                 card->query[card->nquery++] = r;
  172.             else if (data && letter)
  173.                 while (*data) {
  174.                     if ((*data & ~0x20) == letter) {
  175.                         card->query[card->nquery++] = r;
  176.                         break;
  177.                     }
  178.                     if (!pref.allwords)
  179.                         break;
  180.                     while (*data && !BLANK(*data))
  181.                         data++;
  182.                     while (BLANK(*data))
  183.                         data++;
  184.                 }
  185.         }
  186.     if (mask)
  187.         free(mask);
  188. }
  189.  
  190.  
  191. /*
  192.  * collect all cards that match the query expression. The expression is
  193.  * said to match if it does not return an empty string, or a string that,
  194.  * after stripping leading white space, begins with '0', 'f' or 'F'. The
  195.  * F's are supposed to mean False.
  196.  */
  197.  
  198. void query_eval(
  199.     CARD        *card,        /* database and form */
  200.     char        *expr)        /* expression to apply to dbase */
  201. {
  202.     char        *val;        /* return from expression eval */
  203.     char        *mask;        /* for skipping unselected cards */
  204.  
  205.     if (!alloc_query(card, &mask))
  206.         return;
  207.     for (card->row=0; card->row < card->dbase->nrows; card->row++)
  208.         if (SECT_OK(card->dbase, card->row) &&
  209.                         (!mask || mask[card->row])) {
  210.             if (!(val = evaluate(card, expr)))
  211.                 break;
  212.             while (*val == ' ' || *val == '\t') val++;
  213.             if (*val && *val != '0' && *val != 'f' && *val != 'F')
  214.                 card->query[card->nquery++] = card->row;
  215.         }
  216.     if (mask)
  217.         free(mask);
  218. }
  219.  
  220.  
  221. /*
  222.  * prepare for a query: destroy the previous query list, and allocate a new
  223.  * one with enough entries even if all cards match the query. The query list
  224.  * is a list of row numbers in the dbase. Return TRUE if everything went well.
  225.  * If <mask> is nonzero and incremental searches are enabled, create a string
  226.  * with one byte per card, which is 1 if that card was in the previous query.
  227.  * This makes it easy for the search routines to skip cards that have not been
  228.  * in the old query.
  229.  */
  230.  
  231. static BOOL alloc_query(
  232.     CARD        *card,        /* database and form */
  233.     char        **mask)        /* where to store pointer to string */
  234. {
  235.     if (mask) {
  236.         register int r;
  237.         *mask = 0;
  238.         if (pref.incremental && card->query
  239.                      && (*mask = malloc(card->dbase->nrows))) {
  240.             (void)memset(*mask, 0, card->dbase->nrows);
  241.             for (r=0; r < card->nquery; r++)
  242.                 (*mask)[card->query[r]] = 1;
  243.         }
  244.     }
  245.     query_none(card);
  246.     if (!card->dbase->nrows)
  247.         return(FALSE);
  248.     if (!(card->query = (int*)malloc(card->dbase->nrows * sizeof(int*)))) {
  249.         create_error_popup(toplevel, errno,
  250.                     "No memory for query result summary");
  251.         return(FALSE);
  252.     }
  253.     *card->query = 0;
  254.     return(TRUE);
  255. }
  256.